package cn.iocoder.yudao.module.hs.service.picktaskmaterialdetail;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.module.hs.controller.admin.material.vo.MaterialRespVO;
import cn.iocoder.yudao.module.hs.controller.admin.picktaskmaterial.vo.StartPickMaterialVO;
import cn.iocoder.yudao.module.hs.controller.admin.picktaskmaterial.vo.StartPickVO;
import cn.iocoder.yudao.module.hs.controller.admin.picktaskmaterialdetail.vo.PickTaskMaterialDetailPageReqVO;
import cn.iocoder.yudao.module.hs.controller.admin.picktaskmaterialdetail.vo.PickTaskMaterialDetailRespVO;
import cn.iocoder.yudao.module.hs.controller.admin.picktaskmaterialdetail.vo.PickTaskMaterialDetailSaveReqVO;
import cn.iocoder.yudao.module.hs.controller.admin.scan.vo.ScanInfoReqVO;
import cn.iocoder.yudao.module.hs.dal.dataobject.material.MaterialDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.picktask.PickTaskDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.picktaskmaterialdetail.PickTaskMaterialDetailDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.tray.TrayDO;
import cn.iocoder.yudao.module.hs.dal.mysql.picktaskmaterialdetail.PickTaskMaterialDetailMapper;
import cn.iocoder.yudao.module.hs.service.material.MaterialService;
import cn.iocoder.yudao.module.hs.service.materialbox.MaterialBoxService;
import cn.iocoder.yudao.module.hs.service.picktask.PickTaskService;
import cn.iocoder.yudao.module.hs.service.rfid.RfidService;
import cn.iocoder.yudao.module.hs.service.tray.TrayService;
import cn.iocoder.yudao.module.hs.util.RedisUtil;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.hs.enums.ErrorCodeConstants.*;

/**
 * 拣货物料明细 Service 实现类
 *
 * @author 航盛
 */
@Service
@Validated
public class PickTaskMaterialDetailServiceImpl implements PickTaskMaterialDetailService {

    @Resource
    private PickTaskMaterialDetailMapper pickTaskMaterialDetailMapper;
    @Resource
    @Lazy
    private PickTaskService pickTaskService;
    @Resource
    private TrayService trayService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MaterialService materialService;
    @Resource
    private MaterialBoxService materialBoxService;
    @Resource
    private RfidService rfidService;

    @Override
    public Long createPickTaskMaterialDetail(PickTaskMaterialDetailSaveReqVO createReqVO) {
        // 插入
        PickTaskMaterialDetailDO pickTaskMaterialDetail = BeanUtils.toBean(createReqVO, PickTaskMaterialDetailDO.class);
        pickTaskMaterialDetailMapper.insert(pickTaskMaterialDetail);
        // 返回
        return pickTaskMaterialDetail.getId();
    }

    @Override
    public void updatePickTaskMaterialDetail(PickTaskMaterialDetailSaveReqVO updateReqVO) {
        // 校验存在
        validatePickTaskMaterialDetailExists(updateReqVO.getId());
        // 更新
        PickTaskMaterialDetailDO updateObj = BeanUtils.toBean(updateReqVO, PickTaskMaterialDetailDO.class);
        pickTaskMaterialDetailMapper.updateById(updateObj);
    }

    @Override
    public void deletePickTaskMaterialDetail(Long shipOrderId) {
        // 删除
        pickTaskMaterialDetailMapper.delete(PickTaskMaterialDetailDO::getShipOrderId, shipOrderId);
    }

    private void validatePickTaskMaterialDetailExists(Long id) {
        if (pickTaskMaterialDetailMapper.selectById(id) == null) {
            throw exception(PICK_TASK_MATERIAL_DETAIL_NOT_EXISTS);
        }
    }

    @Override
    public PickTaskMaterialDetailDO getPickTaskMaterialDetail(Long id) {
        return pickTaskMaterialDetailMapper.selectById(id);
    }

    @Override
    public PageResult<PickTaskMaterialDetailDO> getPickTaskMaterialDetailPage(PickTaskMaterialDetailPageReqVO pageReqVO) {
        return pickTaskMaterialDetailMapper.selectPage(pageReqVO);
    }

    @Override
    @DataPermission(enable = false)
    public List<PickTaskMaterialDetailDO> getPickTaskMaterialDetailByCode(String code) {
        return pickTaskMaterialDetailMapper.selectListByRfidOrBoxOrMaterial(code);
    }

    @Override
    public List<PickTaskMaterialDetailDO> getPickTaskMaterialDetailByShipOrderId(Long shipOrderId) {
        return pickTaskMaterialDetailMapper.selectListByShipOrderId(shipOrderId);
    }

    @Override
    public List<PickTaskMaterialDetailRespVO> getPickTaskMaterialDetailList(ScanInfoReqVO scanInfoReqVO) {
        PickTaskDO pickTask = pickTaskService.getPickTask(scanInfoReqVO.getPickTaskId());
        if (pickTask == null) {
            throw exception(PICK_TASK_NOT_EXISTS);
        }
        StartPickVO startPick = RedisUtil.getStartPick(pickTask.getCode());
        List<MaterialRespVO> allMaterialList = new ArrayList<>();
        for (StartPickMaterialVO startPickMaterialVO : startPick.getList()) {
            allMaterialList.addAll(startPickMaterialVO.getMaterialRespVOList());
        }
        // 缓存tray和rfid的对应关系
        Map<String, String> trayRfidCodeMap = new HashMap<>();
        List<PickTaskMaterialDetailRespVO> list = new ArrayList<>();
        allMaterialList.forEach(materialRespVO -> {
            PickTaskMaterialDetailRespVO pickTaskMaterialDetailRespVO = new PickTaskMaterialDetailRespVO();
            pickTaskMaterialDetailRespVO.setMaterialName(materialRespVO.getName());
            pickTaskMaterialDetailRespVO.setMaterialCode(materialRespVO.getCode());
            pickTaskMaterialDetailRespVO.setMaterialModeCode(materialRespVO.getModeCode());
            pickTaskMaterialDetailRespVO.setWarehouseId(materialRespVO.getWarehouseId());
            pickTaskMaterialDetailRespVO.setBoxCode(materialRespVO.getBoxCode());
            pickTaskMaterialDetailRespVO.setTrayCode(materialRespVO.getTrayCode());
            if (!trayRfidCodeMap.containsKey(materialRespVO.getTrayCode())) {
                TrayDO tray = trayService.getTray(materialRespVO.getTrayCode());
                if (tray != null) {
                    trayRfidCodeMap.put(tray.getCode(), tray.getRfidCode());
                }
            }
            pickTaskMaterialDetailRespVO.setRfidCode(trayRfidCodeMap.get(materialRespVO.getTrayCode()));
            pickTaskMaterialDetailRespVO.setWarehousingTime(materialRespVO.getWarehousingTime());
            pickTaskMaterialDetailRespVO.setPickStatus(materialRespVO.getPickStatus());
            PickTaskMaterialDetailDO pickTaskMaterialDetail = getPickTaskMaterialDetail(materialRespVO.getCode());
            if (pickTaskMaterialDetail != null) {
                pickTaskMaterialDetailRespVO.setPickStatus(1);
            }
            list.add(pickTaskMaterialDetailRespVO);
        });
        return list;
    }

    @Override
    @Transactional
    public void insertPickTaskMaterialDetail(PickTaskDO pickTask, Long toWarehouseDeptId) {
        List<MaterialDO> selectedMaterials = RedisUtil.getSelectedMaterials(pickTask.getCode());
        if (CollUtil.isEmpty(selectedMaterials)) {
            throw exception(PICK_TASK_MATERIAL_DETAIL_SELECTED_EMPTY);
        }
        Set<String> materialCodeSet = convertSet(selectedMaterials, MaterialDO::getCode);
        List<PickTaskMaterialDetailDO> pickTaskMaterialDetail = getPickTaskMaterialDetail(pickTask.getId(), materialCodeSet);
        if (CollUtil.isNotEmpty(pickTaskMaterialDetail)) {
            throw exception(PICK_TASK_MATERIAL_DETAIL_DUPLICATE);
        }
        AdminUserRespDTO user = adminUserApi.getUser(getLoginUserId());
        // 缓存tray和rfid的对应关系
        Map<String, String> trayRfidCodeMap = new HashMap<>();
        List<PickTaskMaterialDetailDO> list = new ArrayList<>();

        for (MaterialDO selectedMaterial : selectedMaterials) {
            PickTaskMaterialDetailDO pickTaskMaterialDetailDO = new PickTaskMaterialDetailDO();
            pickTaskMaterialDetailDO.setPickTaskId(pickTask.getId());
            pickTaskMaterialDetailDO.setMaterialId(selectedMaterial.getId());
            pickTaskMaterialDetailDO.setMaterialCode(selectedMaterial.getCode());
            pickTaskMaterialDetailDO.setMaterialName(selectedMaterial.getName());
            pickTaskMaterialDetailDO.setMaterialModeCode(selectedMaterial.getModeCode());
            pickTaskMaterialDetailDO.setBoxCode(selectedMaterial.getBoxCode());
            pickTaskMaterialDetailDO.setTrayCode(selectedMaterial.getTrayCode());
            if (!trayRfidCodeMap.containsKey(selectedMaterial.getTrayCode())) {
                TrayDO tray = trayService.getTray(selectedMaterial.getTrayCode());
                if (tray != null) {
                    trayRfidCodeMap.put(tray.getCode(), tray.getRfidCode());
                }
            }
            pickTaskMaterialDetailDO.setRfidCode(trayRfidCodeMap.get(selectedMaterial.getTrayCode()));
            pickTaskMaterialDetailDO.setShipOrderId(pickTask.getShipOrderId());
            pickTaskMaterialDetailDO.setPickUserId(user.getId());
            pickTaskMaterialDetailDO.setPickUserName(user.getNickname());
            pickTaskMaterialDetailDO.setPickTime(LocalDateTime.now());
            list.add(pickTaskMaterialDetailDO);
        }
        pickTaskMaterialDetailMapper.insertBatch(list);

        Set<String> materialCodes = selectedMaterials.stream().map(MaterialDO::getCode).collect(Collectors.toSet());
        materialService.updateMaterialOut(materialCodes, toWarehouseDeptId);
        Set<String> boxCodes = selectedMaterials.stream().map(MaterialDO::getBoxCode).collect(Collectors.toSet());
        materialBoxService.updateMaterialBoxOut(boxCodes, toWarehouseDeptId);
        Set<String> trayCodes = selectedMaterials.stream().map(MaterialDO::getTrayCode).collect(Collectors.toSet());
        trayService.updateTrayOut(trayCodes, toWarehouseDeptId);
        if (ObjUtil.isNotNull(toWarehouseDeptId)) {
            Set<String> rfidCodes = convertSet(list, PickTaskMaterialDetailDO::getRfidCode);
            rfidService.updateRfidOut(rfidCodes, toWarehouseDeptId);
        }
    }

    @Override
    public List<PickTaskMaterialDetailDO> getPickTaskMaterialDetail(Long pickTaskId, Set<String> materialCodeSet) {
        return pickTaskMaterialDetailMapper.selectList(PickTaskMaterialDetailDO::getMaterialCode, materialCodeSet,
                    PickTaskMaterialDetailDO::getPickTaskId, pickTaskId);
    }

    @Override
    public PickTaskMaterialDetailDO getPickTaskMaterialDetail(String materialCode) {
        return pickTaskMaterialDetailMapper.selectOne(
                PickTaskMaterialDetailDO::getMaterialCode, materialCode);
    }

    @Override
    public List<PickTaskMaterialDetailDO> getPickTaskMaterialDetailList(Long shipOrderId) {
        return pickTaskMaterialDetailMapper.selectList(PickTaskMaterialDetailDO::getShipOrderId, shipOrderId);
    }

    @Override
    public void rollbackPickTaskMaterialBoxTray(Long shipOrderId, Long warehouseId) {
        List<PickTaskMaterialDetailDO> pickTaskMaterialDetailList = getPickTaskMaterialDetailList(shipOrderId);
        Set<String> materialCodes = pickTaskMaterialDetailList.stream().map(PickTaskMaterialDetailDO::getMaterialCode).collect(Collectors.toSet());
        materialService.updateMaterialRollback(materialCodes, warehouseId);
        Set<String> boxCodes = pickTaskMaterialDetailList.stream().map(PickTaskMaterialDetailDO::getBoxCode).collect(Collectors.toSet());
        materialBoxService.updateMaterialBoxRollback(boxCodes, warehouseId);
        Set<String> trayCodes = pickTaskMaterialDetailList.stream().map(PickTaskMaterialDetailDO::getTrayCode).collect(Collectors.toSet());
        trayService.updateTrayRollback(trayCodes, warehouseId);
    }
}